<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body style="height: 100vh;display: flex;justify-content: center;align-items: center">
<canvas id="cv" width="500px" height="500px" style="border: 1px saddlebrown solid; pointer:hand;"></canvas>


<!--<div style=" display: flex;justify-content: center;align-items: center">-->
<!--<canvas id="cv1" width="500px" height="500px" style="border: 1px saddlebrown solid;"></canvas>-->
<!--</div>-->
</body>
</html>

<script>
    <!-- 在高铁上没网络很无聊 写出来打发时间 写的很low -->
    const config = {
            cells: 100
        }
    ;

    function Cell(x, y, isdg, indexX, indexY, saolei, ctx) {
        this.x = x;
        this.y = y;
        this.isdg = isdg;
        this.indexX = indexX;
        this.indexY = indexY;
        this.dgNum = isdg ? -1 : 0;
        this.saolei = saolei;
        this.ctx = ctx;

    }

    Cell.prototype.showZhoubian = function (must) {

        this.findZhoubian().forEach(linju => {
            (!linju.isdg || must) && linju.show(false)

        });
    }
    Cell.prototype.show = function (isfist) {

        if (this.isBiaoji) return;
        if (this.isdg) {
            alert('游戏结束');
            location.reload();
        } else if (this.isShow) {
            return;
        } else {

            let width = this.saolei.cellWidth;
            this.ctx.fillStyle = "#fff";
            this.ctx.beginPath();
            this.ctx.fillRect(this.x + 1, this.y + 1, width - 2, width - 2);
            this.ctx.closePath();
            this.ctx.fill();

            if (this.dgNum > 0) {//大于0才显示数字
                this.ctx.fillStyle = "black";
                this.ctx.beginPath();
                this.ctx.fillText(this.dgNum, this.x + 10, this.y + 16)
                this.ctx.closePath();
                this.ctx.fill();
            }
            this.isShow = true;
            if (this.dgNum === 0 || isfist) {
                this.showZhoubian()
            }
        }
    }

    Cell.prototype.findZhoubian = function () {
        var temp = [];
        //周边八个点
        temp.push({x: this.indexX - 1, y: this.indexY});
        temp.push({x: this.indexX + 1, y: this.indexY});
        temp.push({x: this.indexX, y: this.indexY - 1});
        temp.push({x: this.indexX, y: this.indexY + 1});
        temp.push({x: this.indexX - 1, y: this.indexY - 1});
        temp.push({x: this.indexX + 1, y: this.indexY + 1});
        temp.push({x: this.indexX - 1, y: this.indexY + 1});
        temp.push({x: this.indexX + 1, y: this.indexY - 1});
        var rtn = [];
        temp.forEach(lj => {
            if (lj.x >= this.saolei.minIndex && lj.y >= this.saolei.minIndex
                && lj.x < this.saolei.maxIndex && lj.y < this.saolei.maxIndex) {
                rtn.push(this.saolei.findLei(lj.x, lj.y));
            }
        })
        return rtn;
    }

    Cell.prototype.fillCell = function (color) {
        var cellWid = this.saolei.cellWidth;
        this.ctx.beginPath();
        this.ctx.fillStyle = color || "#fff";
        this.ctx.fillRect(this.x + 1, this.y + 1, cellWid - 2, cellWid - 2)
        this.ctx.closePath()
        this.ctx.fill();
    }
    //标记成雷
    Cell.prototype.biaoji = function () {

        if (this.isShow) return;
        if (this.isBiaoji) {
            this.saolei.flagNum--;
            this.fillCell(this.saolei.initColor);
            this.isBiaoji = false;
        } else {
            this.saolei.flagNum++;
            this.ctx.beginPath();
            this.ctx.fillStyle = 'black';
            this.ctx.arc(this.x + 12.5, this.y + 12.5, 8, 0, Math.PI * 2, true)
            this.ctx.closePath();
            this.ctx.fill();
            this.isBiaoji = true;
            this.saolei.isWin();
        }

    }

    //闪烁效果
    Cell.prototype.blink = function () {
        blinkHelper(this.blinkNum || 3, this);
    }

    function blinkHelper(num, cell) {

        console.log('blinkHelper', cell, num);
        //TODO rand color
        let color = num % 2 == 0 ? 'red' : 'green';
        cell.fillCell(color)
        if (num--) {
            setTimeout(function () {
                blinkHelper(num, cell)
            }, 100);
        } else {
            setTimeout(function () {
                cell.fillCell(cell.saolei.initColor)
            }, 100);
        }
    }

    var cvs;
    (function () {
        let domcvs = $("#cv");
        domcvs.onclick = function (e) {
            console.log(e);
            let clickLei = this.saolei.findByClick(e.layerX, e.layerY);
            console.log(clickLei);
            clickLei.show(true)

        }

        domcvs.oncontextmenu = function (e) {
            let biaoji = this.saolei.findByClick(e.layerX, e.layerY);

            if (biaoji.isShow && biaoji.dgNum > 0) {//已经打开 智能开箱
                let countLei = 0;
                let noFlag = [];
                biaoji.findZhoubian().forEach(cel => {
                    if (cel.isBiaoji) countLei++
                    else if (!cel.isShow) noFlag.push(cel)
                });
                if (countLei == biaoji.dgNum) {
                    biaoji.showZhoubian(true);
                } else if (noFlag.length > 0) {
                    noFlag.forEach(cel => cel.blink())
                }
            } else {
                biaoji.biaoji();
            }

            e.preventDefault();
        }
        let a = domcvs.getContext("2d");
        cvs = a;
        a.lineWidth = 1;
        a.strokeStyle = "red";
        a.fillStyle = "black";
        //初始化
        var leis = table(20, 500, a);
        domcvs.saolei = leis;
        // let b = $("#cv1").getContext("2d");
        // var start = 1;
        // var y = 0;
        // var cellWidth = 5;
        // b.strokeStyle = "red";
        // var width = 500;
        // while ((y = (start++ * cellWidth)) < width) {
        //     b.beginPath();
        //     b.moveTo(0, y);
        //     b.lineTo(y, width);
        //     b.stroke();
        //     b.save();
        // }


    })();

    function table(cells, width, g) {
        var start = 1;
        var cellWidth = Math.round(width / cells);
        var y = 0;
        let rtn = [];
        // 当前游戏地雷个数
        rtn.mineNum = 0;
        //当前游戏已经标记个数
        rtn.flagNum = 0;
        rtn.initColor = "#e1e1e1";
        g.fillStyle = rtn.initColor;

        g.fillRect(0, 0, width, width)
        while ((y = (start++ * cellWidth)) < width) {
            g.beginPath();
            g.moveTo(y, 0);
            g.lineTo(y, width);
            g.stroke();
            g.save();

        }
        start = 1;
        while ((y = (start++ * cellWidth)) < width) {
            g.beginPath();
            g.moveTo(0, y);
            g.lineTo(width, y);
            g.stroke();
            g.save();
        }
        start = start - 1;
        for (var i = 0; i < start; i++) {
            let arr = [];
            for (var j = 0; j < start; j++) {
                let isdg = Math.random() > .8
                if (isdg) rtn.mineNum++;
                arr.push(new Cell(i * cellWidth, j * cellWidth, isdg, i, j, rtn, g));
            }
            rtn.push(arr);
        }
        rtn.objCount = start * start;
        rtn.maxIndex = start;
        rtn.minIndex = 0;
        rtn.cellWidth = cellWidth;
        rtn.findLei = function (x, y) {
            return this[x][y];
        }
        rtn.calcDgNum = function () {
            this.forEach(arr => arr.forEach(lei => {
                this.leiPostProcess(lei);
            }))
        }
        rtn.leiPostProcess = function (lei) {
            if (lei.isdg) {
                //找到附近的单元格对象 并计数
                lei.findZhoubian().forEach(linjuObj => !linjuObj.isdg && linjuObj.dgNum++);

            }
        }
        rtn.findByClick = function (layerX, layerY) {
            var x = Math.floor(layerX / this.cellWidth);
            var y = Math.floor(layerY / this.cellWidth);
            return this.findLei(x, y);
        }

        // 通过过滤查找满足条件的Cell对象
        rtn.findByFilter = function (filter) {
            let results = [];
            this.forEach(arr => arr.forEach(mine => filter(mine) && results.push(mime)))
            return results;
        }
        rtn.isWin = function () {
            //雷的数量 与标记的数量相等
            if (this.mineNum == this.flagNum) {
                if (this.findByFilter(mine => {
                    return mine.isBiaoji && mine.isdg;
                }).length == this.flagNum) {
                    let now = new Date();
                    let timing = (now.getTime() - this.startTime.getTime());
                    alert(`游戏胜利！耗时：${timing}s`);
                }
            }
        }
        rtn.calcDgNum();
        rtn.startTime = new Date();
        return rtn;
    }

    function $(selector) {
        return document.querySelector(selector);
    }
</script>